#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

#define CAPACITY 4
int buffer1[CAPACITY];
int buffer2[CAPACITY];
int in1,in2;
int out1,out2;

int buffer_is_empty(int aim)
{
	if(aim == 1) return in1 == out1;
	else return in2 == out2;
}

int buffer_is_full(int aim)
{
	if(aim == 1) return (in1 + 1) % CAPACITY == out1;
	else return (in2 + 1) % CAPACITY == out2;
}

int get_item(int aim)
{
	int item;

	if(aim == 1) { 
		item = buffer1[out1];
		out1 = (out1 + 1) % CAPACITY;
	}
	else {
		item = buffer2[out2];
		out2 = (out2 + 1) % CAPACITY;
	}
	return item;
}

void put_item(int item, int aim)
{
	if(aim == 1) {
		buffer1[in1] = item;
		in1 = (in1 + 1) % CAPACITY;
	}
	else {
		buffer2[in2] = item;
		in2 = (in2 + 1) % CAPACITY;
	}
}

typedef struct {
	int value;
	pthread_mutex_t mutex;
	pthread_cond_t cond;
} sema_t;

void sema_init(sema_t *sema, int value)
{
	sema->value = value;
	pthread_mutex_init(&sema->mutex, NULL);
	pthread_cond_init(&sema->cond, NULL);
}

void sema_wait(sema_t *sema)
{
	pthread_mutex_lock(&sema->mutex);
	while(sema->value <= 0)
		pthread_cond_wait(&sema->cond, &sema->mutex);
	sema->value -= 1;
	pthread_mutex_unlock(&sema->mutex);
}

void sema_signal(sema_t *sema)
{
	pthread_mutex_lock(&sema->mutex);
	sema->value += 1;
	pthread_cond_signal(&sema->cond);
	pthread_mutex_unlock(&sema->mutex);
}

sema_t mutex1,mutex2;
sema_t empty1,empty2;
sema_t full1,full2;

#define ITEM_COUNT (CAPACITY) * 2

void *consume(void *arg)
{
	int i;
	int item;

	for(i = 0; i < ITEM_COUNT; i++) {
		sema_wait(&full2);
		sema_wait(&mutex2);

		item = get_item(2);
		printf("		consume item: %c\n", item);

		sema_signal(&mutex2);
		sema_signal(&empty2);
	}
	return NULL;
}

void *produce(void *arg)
{
	int i;
	int item;

	for(i = 0; i < ITEM_COUNT; i++) {
		sema_wait(&empty1);
		sema_wait(&mutex1);

		item = 'a' + i;
		put_item(item, 1);
		printf("produce item: %c\n", item);

		sema_signal(&mutex1);
		sema_signal(&full1);
	}
	return NULL;
}

void *transfer(void *arg)
{
	int i;
	int item;

	for(i = 0; i < ITEM_COUNT; i++) {
		sema_wait(&full1);
		sema_wait(&mutex1);

		item = get_item(1);
		item = item - 32;
		printf("	transfer item: %c\n", item);

		sema_signal(&mutex1);
		sema_signal(&empty1);

		sema_wait(&empty2);
		sema_wait(&mutex2);

		put_item(item, 2);
		//printf("	transfer item: %c\n", item);

		sema_signal(&mutex2);
		sema_signal(&full2);
	}
	return NULL;
}

int main()
{
	pthread_t transfer_tid;
	pthread_t consumer_tid;

	sema_init(&mutex1, 1);
	sema_init(&mutex2, 1);

	sema_init(&empty1, CAPACITY - 1);
	sema_init(&full1, 0);

	sema_init(&empty2, CAPACITY - 1);
	sema_init(&full2, 0);

	pthread_create(&transfer_tid, NULL, transfer, NULL);
	pthread_create(&consumer_tid, NULL, consume, NULL);
	produce(NULL);
	pthread_join(transfer_tid, NULL);
	pthread_join(consumer_tid, NULL);

	return 0;
}

